package absolute_zero.com.urbox.transmition;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import android.util.Log;
import android.widget.ProgressBar;
/**
 * Transmition Client for the Android P2P application
 * 
 * @author Charles C. Francis
 * @version 0.01a
 *
 */
public class Client extends Thread
{
	
	/*****************************************************
	 * FIELDS											 *
	 *****************************************************/
	
	/**
	 * flag to use the Name Address
	 */
	static final int NAME_ADDRESS = 0;
	/**
	 * flag to use the IP Address
	 */
	static final int IP_ADDRESS = 1;
	/**
	 * Default port the client sends to
	 * @since 0.01a
	 */
	static final int DEFAULT_PORT = 5075;
	/**
	 * Specified port the client sends to.
	 * <br>If _port is set to 0, Client uses the Default Port.
	 * @since 0.01a
	 */
	private int _port = 0;
	/**
	 * This address is used if the connection address is given as a name rather than a literal IP address. <br>
	 * if _flags bit 0 is set to 0 (default), the program uses this address.
	 * @since 0.01a
	 */
	private String _Saddress = "";
	/**
	 * This address is used if the connection address is given as a literal IP address.<br>
	 * if _flags bit 0 is set to 1, the program uses this address
	 * @since 0.01a
	 */
	private byte[] _Baddress;
	/**
	 * The time in miliseconds the client will wait for a response from the server.
	 * @since 0.01a
	 */
	private int _timeout = 3000;
	/**
	 * Processing flags used in the code<br>
	 * Bit 0: Address Type<br>
	 * <t>0 - Name Address<br>
	 * <t>1 - Raw IP Address<br>
	 * @since 0.01a
	 */
	protected int _flags = 0;
	/**
	 * The name of the file to be transfered
	 * @since 0.01a
	 */
	protected String _file = "";
	/**
	 * Whether or not the file has succeded in transfering
	 */
	protected boolean _success = false;
	protected int filesize;
	private File transArray;
	
	/****************************************
	 * CONSTRUCTORS							*
	 ****************************************/
	
	/**
	 * readys the client to send "file" to "Address" on the default port
	 * @param file name of the file to send
	 * @param Address address to send the file to
	 */
	public Client(String file, String Address)
	{
		_file = file;
		_Saddress = Address;
	}
	/**
	 * readys the client to send "file" to "Address" on port "Port"
	 * @param file name of the file to send
	 * @param Address address to send the file to
	 * @param Port port to connect to
	 * @param mprogress 
	 */
	public Client(String file, String Address, int Port)
	{
		this(file, Address);
		_port = Port;
	}
	/**
	 * readys the client to send "file" to "Address" on the default port
	 * @param file name of the file to send
	 * @param Address address to send the file to
	 */
	public Client(String file, byte[] Address)
	{
		_file = file;
		_Baddress = Address;
		this.setFlagBit(0);
	}
	/**
	 * readys the client to send "file" to "Address" on port "Port"
	 * @param file name of the file to send
	 * @param Address address to send the file to
	 * @param Port port to connect to
	 */
	public Client(String file, byte[] Address, int Port)
	{
		this(file, Address);
		_port = Port;
	}
	
	public int getFileSize(){
		return (int) transArray.length();
	}
	
	/****************************************
	 * METHODS								*
	 ****************************************/
	
	/**
	 * Opens the socket, and attempts to connect to the server.  If connection is a success, 
	 * transfers file to receiving server.
	 */
	@Override
	public void run() {
		InetAddress ia = null;
		Socket _socket = null;
		try
		{
			//Check which address to use
			if(checkFlagBit(0))
			{
				ia = InetAddress.getByAddress(this._Baddress);
			}
			else
			{
				ia = InetAddress.getByName(this._Saddress);
			}
			//if the address didn't set, then something is wrong.  terminate w/o sending
			if(ia == null)
			{
				return;
			}
			//if the port is unset, set it to the default
			if(_port == 0)
			{
				_port = DEFAULT_PORT;
			}
			//create the address and connect the socket
			SocketAddress addr = new InetSocketAddress(ia, _port);
			_socket = new Socket();
			_socket.connect(addr, _timeout);
		}catch (UnknownHostException UHE){ 
			Log.d("UnknownHostException", UHE.toString());
			return;} 
		 catch (SocketTimeoutException STI){ 
				Log.d("SocketTimeoutException", STI.toString());
				return;}
		 catch (IOException e) { 
				Log.d("IOException", e.toString());
				return;}
		//find the file
		 Log.d("Finding File", this._file);
		File transfer = new File (this._file);
		//create the byte array to transfer
		byte [] transArray = new byte [(int)transfer.length()];
		//open the FileStream to get the data
		FileInputStream fis = null;
		try 
		{
			fis = new FileInputStream(transfer);
		} catch (FileNotFoundException e) 
		{
			Log.d("IOException", e.toString());
			//close the socket if something goes wrong
			try 
			{
				_socket.close();
			} catch (IOException e1) {
				Log.d("IOException", e1.toString());
			}
			return;
		}
		//create the final writing stream
		BufferedInputStream bis = new BufferedInputStream(fis, 1024);
		try 
		{
			//send the name and length to the receiver
			BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(_socket.getOutputStream()), 1024);
			Log.d("I think I'm sending", _file.split("/")[_file.split("/").length - 1]);
			wr.write(_file.split("/")[_file.split("/").length - 1] + "\n");
			wr.write(transfer.length() + "\n");
			wr.flush();
			//copy the data into the byte array
			bis.read(transArray, 0, transArray.length);
			//set the output stream
			OutputStream outStr = _socket.getOutputStream();
			//put the file in the buffer
			outStr.write(transArray, 0, transArray.length);
			//send the file
			outStr.flush();
			//the file successfully transfered
			Log.d("File sent", "send");
			_success = true;
			//close the socket
			_socket.close();
		} catch (IOException e) 
		{
			Log.d("IOException", e.toString());
			//close the socket if something goes wrong
			try 
			{
				_socket.close();
			} catch (IOException e1){
				Log.d("IOException", e1.toString());
			}
			return;
		}
		
		
		
	} 
	/**
	 * resets the flag bit "i" to 0
	 * @param i the bit to set to 0
	 */
	private void resetFlagBit(int i)
	{
		int mask = 1;
		mask <<= (i - 1);
		_flags &= ~mask;
	}
	/**
	 * Sets the flag bit "i" to 1
	 * @param i - bit to set to 1
	 */
	private void setFlagBit(int i)
	{
		int mask = 1;
		mask <<= (i - 1);
		_flags |= mask;
	}
	
	/**
	 * toggles the flag bit "i" to 0/1 if it is 1/0
	 * @param i bit to flip
	 */
	@SuppressWarnings("unused")
	private void toggleFlagBit(int i)
	{
		int mask = 1;
		mask <<= (i - 1);
		_flags ^= mask;
	}
	/**
	 * checks whether the bit "i" in the flag is 1
	 * @param i bit to check
	 * @return true if bit "i" is one, otherwise, false
	 */
	private boolean checkFlagBit(int i)
	{
		int check = (_flags >> (i - 1)) & 1;
		if(check == 1)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	/**
	 * Whether or not to use raw IP Address
	 * @param use true to use raw IP, false to use Name Address
	 */
	public void useIPAddress(boolean use)
	{
		resetFlagBit(0);
		if (use)
		{
			setFlagBit(0);
		}
	}
	/**
	 * Checks if the file transfered correctly
	 * @return true - the file was transfered, false otherwise
	 */
	public boolean failure()
	{
		if(this.getState() == Thread.State.TERMINATED && !this._success)
		{
			return true;
		}
		return false;
	}
	
	
	/******************
	 * MUTATORS       *
	 ******************/
	
	/**
	 * sets the port used to "i"
	 * @param i port to use
	 */
	public void setPort(int i)
	{
		_port = i;
	}
	/**
	 * gets the currently set port
	 * @return the port the connection will use
	 */
	public int getPort()
	{
		return _port;
	}
	/**
	 * Sets the Name Address
	 * @param s the Name Address
	 */
	public void setAddress(String s)
	{
		_Saddress = s;
	}
	/**
	 * Gets the Name Address
	 * @return the Name Address
	 */
	public String getNameAddress()
	{
		return _Saddress;
	}
	/**
	 * Sets the Raw IP address<br>NOTE: for the program to use this,
	 * the method uesIPAddress(true) must be called
	 * @param b the IP address
	 */
	public void setAddress(byte[] b)
	{
		if (b.length != 4)
		{
			
		}
		else
		{
			_Baddress = b;
		}
	}
	/**
	 * Gets the IP Address
	 * @return the IP Address
	 */
	public byte[] getIPAddress()
	{
		return _Baddress;
	}
	/**
	 * Sets the connection timeout
	 * @param i time( in miliseconds) to wait for a connection before timing out
	 */
	public void setTimeout(int i)
	{
		_timeout = i;
	}
	/**
	 * Gets the time the socket will wait for a connection
	 * @return timeout duration ( in miliseconds) 
	 */
	public int getTimeout()
	{
		return _timeout;
	}
	/**
	 * sets the Name/Location of the file to transfer
	 * @param s file name
	 */
	public void setFile(String s)
	{
		_file = s;
	}
	/**
	 * Gets the currently set file name
	 * @return the file name
	 */
	public String getFile()
	{
		return _file;
	}
	
}
